package com.edushare.common.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.query.Ordering;
import org.apache.cayenne.query.SQLTemplate;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.query.SortOrder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.edushare.common.orm.ORMInterface;
import com.edushare.common.vo.Assignments;
import com.edushare.common.vo.CourseInstances;
import com.edushare.common.vo.Enrolments;
import com.edushare.common.vo.UserAssignmentLink;
import com.edushare.common.vo.Users;

public class UserAssignmentLinkDAO extends CayenneBasedDAO {
	
	private static Log log = LogFactory.getLog(UserAssignmentLinkDAO.class);

	public UserAssignmentLinkDAO(ORMInterface ormInterface) {
		super(ormInterface);
	}

	public void linkToUsers(Assignments assignment, CourseInstances courseInstance, Users creator) {
		
		/*Link to assignee if not in DRAFT mode*/
		if (assignment.getStatus()!= null && assignment.getStatus()!=Assignments.ASSIGNMENT_STATUS_DRAFT) 
		{
			linkToAssignees(assignment);
		}
		
		/* link to assigner */
		UserAssignmentLink ual = getDataContext().newObject(UserAssignmentLink.class);
		ual.setUser(creator);
		ual.setAssignment(assignment);
		ual.setAssignmentRole(UserAssignmentLink.ASSIGNER);
		ual.setStatus(UserAssignmentLink.STATUS_SUBMISSION_NONE);
		ual.setShowAlert(UserAssignmentLink.ALERT_SHOW);
		
		getDataContext().commitChanges();
	}
	
	public UserAssignmentLink getUserAssignmentLink(Users u, Assignments a){
		Expression exp = Expression.fromString("user = $u and assignment = $a");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("u", u);
		params.put("a", a);
		SelectQuery query = new SelectQuery(UserAssignmentLink.class, exp.expWithParameters(params));
		List<UserAssignmentLink> ual = getDataContext().performQuery(query);
		if(ual == null || ual.size() < 1){
			return null;
		}else{
			return ual.get(0);
		}
	}

	public List<UserAssignmentLink> getSubmittersList(Assignments assn, Integer submissionStatus, Integer evalStatus) {
		Expression exp = Expression.fromString("assignment = $assn and assignmentRole = $role and evaluationStat = $eval and status = $stts");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("assn", assn);
		params.put("role", UserAssignmentLink.ASSIGNEE);
		if(submissionStatus!=null && submissionStatus > 0){
			params.put("stts", submissionStatus);			
		}
		if(evalStatus!=null && evalStatus > 0){
			params.put("eval", evalStatus);
		}
		
		SelectQuery query = new SelectQuery(UserAssignmentLink.class, exp.expWithParameters(params));
		return getDataContext().performQuery(query);
	}

	public void deleteUserSubmissions(UserAssignmentLink ual) {
		log.debug("--- Deleting user submission ---");
		//1. delete all of his file
		new AssignmentsDAO(ormInterface).deleteUserSubmittedFiles(ual.getUser(), ual.getAssignment());
		
		//2. delete his link
		getDataContext().deleteObject(ual);
	}
	
	/** returns how many in a assignment are pending, late submissions, in-time ect. */
	public HashMap<Integer, Integer> getSubmissionsStats(Assignments assn){
		HashMap<Integer, Integer> result = new HashMap<Integer, Integer>();
		String countQuery = "select status, #result('count(*)' 'int' 'stts') " +
							"from USER_ASSIGNMENT_LINK " +
							"where assignment_id = #bind($assnId) " +
							"and assignment_role = #bind($assnrole) " +
							"group by status";
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("assnId", assn.getAssignmentId());
		params.put("assnrole", UserAssignmentLink.ASSIGNEE);
		SQLTemplate query = new SQLTemplate(UserAssignmentLink.class, countQuery);
		query.setFetchingDataRows(true);
		query.setParameters(params);
		List<Map> rows = (List<Map>)getDataContext().performQuery(query);
		if(rows!=null){
			for(Map m: rows){
				result.put((Integer)m.get("status"), (Integer)m.get("stts"));
			}
		}
		return result;
	}
	
	public HashMap<Integer, Integer> userAssignmentStats(Users u){
		HashMap<Integer, Integer> result = new HashMap<Integer, Integer>();
		String countQuery = 
				"select " +
					"a.status, #result('count(*)' 'int' 'stts') " +
				"from " +
					"USER_ASSIGNMENT_LINK ual, ASSIGNMENTS a " +
				"where " +
					"ual.assignment_id = a.assignment_id " +
					"and " +
					"ual.user_id=#bind($usrId) " +
				"group by " +
					"a.status";
		
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("usrId", u.getUserId());
		SQLTemplate query = new SQLTemplate(UserAssignmentLink.class, countQuery);
		query.setFetchingDataRows(true);
		query.setParameters(params);
		List<Map> rows = (List<Map>)getDataContext().performQuery(query);
		if(rows!=null){
			for(Map m: rows){
				result.put((Integer)m.get("status"), (Integer)m.get("stts"));
			}
		}
		return result;
	}

	public List<UserAssignmentLink> getAssignments(Users user, Integer roleType) {
		Expression exp = Expression.fromString("user = $usr and assignmentRole = $role");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("usr", user);
		params.put("role", roleType);
		SelectQuery query = new SelectQuery(UserAssignmentLink.class, exp.expWithParameters(params));
		ArrayList<Ordering> orderings = new ArrayList<Ordering>();
		orderings.add(new Ordering("assignment.status", SortOrder.DESCENDING));
		orderings.add(new Ordering("assignment.endDate", SortOrder.ASCENDING));
		query.addOrderings(orderings);
		List<UserAssignmentLink> ual = getDataContext().performQuery(query);
		return ual;
	}

	public void linkToAssignees(Assignments assignment) {
		
		// link to regular students
		List<Enrolments> enrolledStudents = 
						new EnrolmentsDAO(ormInterface)
							.getEnrolmentsByRole(
										assignment.getCourseInstance(), 
										Enrolments.STUDENT);
		
		for (Enrolments e : enrolledStudents) {
			linkToAssignee(e, assignment);
		}
		
		//link to audit students
		List<Enrolments> enrolledAudits = 
			new EnrolmentsDAO(ormInterface)
				.getEnrolmentsByRole(
							assignment.getCourseInstance(), 
							Enrolments.OBSERVER);
		for (Enrolments e : enrolledAudits) {
			linkToAssignee(e, assignment);
		}
		
	}
	
	public void linkToAssignee(Enrolments enrolment, Assignments assignment){
		UserAssignmentLink ual = getDataContext().newObject(
				UserAssignmentLink.class);
		ual.setUser(enrolment.getUser());
		ual.setAssignment(assignment);
		ual.setAssignmentRole(UserAssignmentLink.ASSIGNEE);
		ual.setStatus(UserAssignmentLink.STATUS_SUBMISSION_PENDING);
		ual.setEvaluationStat(UserAssignmentLink.NOT_EVALUATED);
	}

	public boolean isAssignee(Users loggedInUser) {
		Expression exp = Expression.fromString("user = $u and assignmentRole = $role");
		HashMap<String, Object> parameters = new HashMap<String, Object>();
		parameters.put("u", loggedInUser);
		parameters.put("role", UserAssignmentLink.ASSIGNEE);
		SelectQuery query = new SelectQuery(UserAssignmentLink.class, exp.expWithParameters(parameters));
		List<UserAssignmentLink> uall = getDataContext().performQuery(query);
		if(uall != null && uall.size()>0){
			return true;
		}
		return false;
	}

	public void assignOpenToNewEnrolment(Enrolments enrolment) {
		/*if(!isAssignee(enrolment.getUser()))
			return;*/
		CourseInstances cI = enrolment.getCourseInstance();
		List<Assignments> openAssignments = new AssignmentsDAO(ormInterface).getAssignmentsByStatus(cI, Assignments.ASSIGNMENT_STATUS_OPEN);
		for(Assignments assignment: openAssignments){
			if(!isAlreadyAssigned(enrolment.getUser(), assignment)){//assign if not already assigned
				linkToAssignee(enrolment, assignment);
			}
		}
		
		getDataContext().commitChanges();
	}

	public boolean isAlreadyAssigned(Users user, Assignments assignment) {
		Expression exp = Expression.fromString("user = $usr and assignment = $assn");
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("usr", user);
		params.put("assn", assignment);
		SelectQuery query = new SelectQuery(UserAssignmentLink.class, exp.expWithParameters(params));
		List<UserAssignmentLink> uall = getDataContext().performQuery(query);
		if(uall!=null && uall.size()>0){
			return true;
		}
		return false;
	}

}
